/*
 *
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package algoGenetico;

import Rota.Mapa;
import Selecao.AUEstocastica;
import Selecao.Roleta;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 *
 * @author Fernando
 */
public class ControleAG {

    private Populacao populacaoAtual;
    private Populacao populacaoInter;
    private Populacao populacaoProx;
    private Mapa mapa;
    private int tamanhoPopulacao;
    private int origem, destino;
    private double taxaCruzamento;
    private double taxaMutacao;
    private boolean roleta;
    private OperadoresGeneticos operadores = new OperadoresGeneticos();

    //retorna o melhor individuo
    public ControleAG(Mapa mapa, int origem, int destino) {
        
        this.destino = destino;
        this.origem = origem;
        this.mapa = mapa;
        populacaoAtual = new Populacao(mapa);
//        this.populacaoInicial();
    }

    public Individuo getMelhorIndividuo(double taxaCruzamento, double taxaMutacao, int tamanho, boolean roleta) {
        this.tamanhoPopulacao = tamanho;
        this.populacaoInicial();
        this.taxaCruzamento = taxaCruzamento;
        this.taxaMutacao = taxaMutacao;
        this.roleta = roleta;
        double fitnessAnt = 0;
        double fitnessNovo = 0;
        int i = 0;
        do {
            fitnessAnt = fitnessNovo;
            i++;
            System.out.println("\n\n\n");
//            System.out.println("================== Cruzamento " + i + "==================");
            this.cruzamento();
//            System.out.println("================== Mutacao " + i + "==================");
            this.mutacao();
            fitnessNovo = populacaoAtual.getFitnessPopulacao();
            System.out.println("Geracao " + i + ", Fitness = " + populacaoAtual.getFitnessPopulacao());
        } while (fitnessNovo - fitnessAnt != 0.0);
        return populacaoAtual.getIndividuo().get(0);
    }

    public void populacaoInicial() {
        populacaoAtual.gerarPopulacaoInicial(tamanhoPopulacao, origem, destino);
        populacaoAtual.atualizaFitness();

//        populacaoAtual.imprime();
    }

    public int[] selecao(List<Individuo> individuo, int num) {
        if (roleta) {
            return new Roleta().selecao(individuo, num);
        } else {
            return new AUEstocastica().selecao(individuo, num);
        }
    }

    public void cruzamento() {
        populacaoInter = new Populacao(mapa);
        populacaoProx = new Populacao(mapa);
        populacaoInter = populacaoAtual; //populacao intermediaria eh igual a atual
//        populacaoInter = populacaoAtual; //populacao intermediaria eh igual a atual
        int numDeIndividuos = (int) (taxaCruzamento * tamanhoPopulacao);
        int[] selecao = this.selecao(populacaoAtual.getIndividuo(), numDeIndividuos);
        int aux;
        if (selecao.length % 2 == 0) {
            aux = selecao.length;
        } else {
            aux = selecao.length - 1;
        }
        for (int i = 0; i < aux; i++) {
            Individuo a = populacaoAtual.getIndividuo().get(selecao[i]);
            Individuo b = populacaoAtual.getIndividuo().get(selecao[i + 1]);
            i++;
            if (a.getCromossomo().getGenes().size() <= 2 || b.getCromossomo().getGenes().size() <= 2) {
                ArrayList<Individuo> auxi = operadores.cruzamento(a, b);
                populacaoInter.getIndividuo().add(auxi.get(0));
                populacaoInter.getIndividuo().add(auxi.get(1));
            } else {
//                System.out.println("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
            }
        }
        populacaoInter.atualizaFitness();
        Collections.sort(populacaoInter.getIndividuo(), new Comparador()); //ordena os individuos pelo fitness
        //em ordem crescente


        for (int x = 0; x < tamanhoPopulacao; x++) {
            populacaoProx.getIndividuo().add(populacaoInter.getIndividuo().get(x));
        }
        populacaoAtual.getIndividuo().clear();
        populacaoAtual = populacaoProx;
        populacaoInter = populacaoAtual;
//        populacaoAtual.imprime();
    }

    public void mutacao() {
        Populacao populacaoProxi = new Populacao(mapa);
        populacaoInter = new Populacao(mapa);
//        populacaoProx = new Populacao(mapa);
        populacaoInter = populacaoAtual; //populacao intermediaria eh igual a atual
        int numDeIndividuos = (int) (taxaMutacao * tamanhoPopulacao);
        int[] selecao = this.selecao(populacaoAtual.getIndividuo(), numDeIndividuos);
        int aux;
        if (selecao.length % 2 == 0) {
            aux = selecao.length;
        } else {
            aux = selecao.length - 1;
        }
        for (int i = 0; i < aux; i++) {
            Individuo a = populacaoAtual.getIndividuo().get(selecao[i]);
//            System.out.println("indiv " + i);
//            for (int y = 0; y < a.getCromossomo().getTam(); y++) {
//                System.out.print(a.getCromossomo().getGene(y) + "|");
//            }
//            System.out.println("");
            Individuo auxi = operadores.mutacaoPorInsercao(a.getCromossomo(), mapa.mapa.size());

//            System.out.println("Mutacao indiv " + i);
//            for (int y = 0; y < auxi.getCromossomo().getTam(); y++) {
//                System.out.print(auxi.getCromossomo().getGene(y) + "|");
//            }
//            System.out.println("");
            populacaoInter.getIndividuo().add(auxi);
        }
        populacaoInter.atualizaFitness();
        Collections.sort(populacaoInter.getIndividuo(), new Comparador()); //ordena os individuos pelo fitness
        //em ordem crescente

//        populacaoInter.imprime();

        while (populacaoInter.getIndividuo().size() > tamanhoPopulacao) {
            populacaoInter.getIndividuo().remove(populacaoInter.getIndividuo().size() - 1);
        }
        populacaoAtual = populacaoInter;
//        populacaoAtual.imprime();
    }

    class Comparador implements Comparator {

        public int compare(Object obj1, Object obj2) {
            Individuo c1 = (Individuo) obj1;
            Individuo c2 = (Individuo) obj2;
            return (int) (c1.getFitness() - c2.getFitness());
        }
    }
}
